<?php
/**
 * Created by PhpStorm.
 * User: xmy2
 * Date: 18-8-11
 * Time: 下午12:21
 */

namespace vola\tpl;


trait Query
{
    private function parseVar($content = '')
    {
        if (is_numeric($content)) {
            return $content;
        }

        if (strpos($content, '(')) {
            return $this->parseFunction($content);
        }

        if (strpos($content, '??')) {
            return $this->parseOr($content);
        }

        $is_object = false;
        if (strpos($content, '->') !== false) {
            $is_object = true;
            $content = str_replace('->', '.', $content);
        }

        return preg_replace_callback('/[a-zA-Z_](?>\w*)(?:[\.][0-9a-zA-Z_](?>\w*))+|[a-zA-Z_](?>\w*)/i', function ($m) use ($is_object) {
            $copy = $m[0];

            if (isset($this->com[hash('sha256', $copy)])) {
                return $this->com[hash('sha256', $copy)];
            }

            if (strpos($m[0], '[') !== false) {
                $m[0] = preg_replace_callback('/\[(.*?)\]/i', function ($match) {
                    if (strpos($match[1], '"') !== false || strpos($match[1], '\'') !== false) {
                        return $match[0];
                    }
                    return $this->parseVar($match[1]);
                }, $m[0]);
            }

            if ($is_object) {
                $data = '$' . str_replace('.', '->', trim($m[0]));
                $this->com[hash('sha256', $copy)] = $data;
                return $data;
            }


            $var = explode('.', $m[0]);

            $data = '$' . array_shift($var);

            foreach ($var as $r) {
                $data .= "['{$r}']";
            }
            $this->com[hash('sha256', $copy)] = $data;
            return $data;
        }, $content);
    }

    private function parseOr($content) {
        $par = explode('??', $content, 2);
        $code0 = $par[0];
        $code1 = $par[1];
        return $this->parseVar($code0 . ' ? ' . $code0 . ':' . $code1);
    }

    private function parseFunction($content = '')
    {
        $tag = \vola\Container::get('config')->get('tpl.filter_func');
        $tag = explode('|', $tag);
        return preg_replace_callback('/(?P<func>[a-zA-Z_](?>\w*))\((?P<args>.*)\)/i', function ($m) use ($tag) {
            $copy = $m[0];
            if (isset($this->com[hash('sha256', $copy)])) {
                return $this->com[hash('sha256', $copy)];
            }
            $arg = trim($m['args']);
            $function = trim($m['func']);

            if (!empty($arg)) {
                $arg = explode(',', $arg);
                foreach ($arg as $num => $a) {
                    if (strpos($a, '"') !== false || strpos($a, '\'') !== false || strpos($a, '=>') !== false) {
                        $arg[$num] = $this->parseArray(trim($a));
                    } else {
                        $arg[$num] = trim($this->parseVar($a));
                    }
                }
            } else {
                $arg = [];
            }

            $data = "{$function}(" . implode(',', $arg) . ")";
            if (!empty($tag)) {
                foreach ($tag as $func) {
                    if (!empty($func) && preg_match('/' . $func . '(?:[\s])?\(/i', $data)) {
                        $data = preg_replace('/' . $func . '(?:[\s])?\((.*?)\)/i', '$1', $data);
                    }
                }
            }
            $this->com[hash('sha256', $copy)] = $data;
            return $data;
        }, $content);
    }

    private function parseArray($content)
    {
        if (strpos($content, '=>') === false) {
            return $content;
        }
        return preg_replace_callback('/(.*?)\=\>(.*?),|(.*?)\=\>(.*?)$/i', function ($m) {
            $con = '';
            if (!empty($m[3])) {
                if (strpos($m[3], '"') !== false || strpos($m[3], "'") !== false || is_numeric($m[3])) {
                    $con .= $m[3] . ' => ';
                } else {
                    $con .= $this->parseVar($m[3]) . ' => ';
                }
                if (strpos($m[4], '"') !== false || strpos($m[4], "'") !== false || is_numeric($m[4])) {
                    $con .= $m[4];
                } else {
                    $con .= $this->parseVar($m[4]);
                }
            } else {
                if (strpos($m[1], '"') !== false || strpos($m[1], "'") !== false || is_numeric($m[1])) {
                    $con .= $m[1] . ' => ';
                } else {
                    $con .= $this->parseVar($m[1]) . ' => ';
                }
                if (strpos($m[2], '"') !== false || strpos($m[2], "'") !== false || is_numeric($m[2])) {
                    $con .= $m[2];
                } else {
                    $con .= $this->parseVar($m[2]);
                }
            }
            return $con;
        }, $content);
    }

    /**
     * foreach方法
     * @param string $content
     * @return mixed
     */
    public function foreachCommand($content = '')
    {
        return preg_replace_callback('/@for(?:[\s])([\w\W]*?)in(?:[\s])([\w\W]*?)(?:[\s]*)\:|@endfor;/is', function ($match) {
            if (isset($this->com[md5($match[0])])) {
                return $this->com[md5($match[0])];
            }
            if (strpos($match[0], '@endfor') === 0) {
                return '<?php endforeach;endif; ?>';
            }
            if (strpos($match[1], ',')) {
                $match[1] = explode(',', $match[1]);
                foreach ($match[1] as &$v) {
                    $v = trim($v);
                }
                $match[1] = implode(' => $', $match[1]);
            }
            $match[1] = '$' . trim($match[1]);
            $match[2] = $this->parseVar($match[2]);
            // 转换变量
            //end
            $return = '<?php if(is_array(' . $match[2] . ')';
            if (class_exists('\\yang\\model\\Result', false)) {
                $return .= '|| ' . $match[2] . ' instanceof \yang\model\Result';
            }
            $return .= '): $__LIST__ = ' . $match[2] . ';foreach ($__LIST__ as ' . $match[1] . '): ?>';
            $this->com[md5($match[0])] = $return;
            return $return;
        }, $content);
    }

    /**
     * for方法
     * @param string $content
     * @return mixed
     */
    public function forCommand($content = '')
    {
        return preg_replace_callback('/@for(?:[\s])([\w\W]*?)as\s+range\(([\w\W]*?)\)(?:[\s]*):|@endf;/is', function ($match) {
            if (isset($this->com[md5($match[0])])) {
                return $this->com[md5($match[0])];
            }
            if (strpos($match[0], '@endf') === 0) {
                return '<?php endfor; ?>';
            }
            $var = $this->parseVar($match[1]);
            $range = $match[2];
            $range = explode(',', $range);

            foreach ($range as &$item) {
                $item = trim($item);
                if (is_numeric($item)) {
                    $item = intval($item);
                } else {
                    $item = $this->parseVar($item);
                }
            }

            if (count($range) < 3) {
                $range[2] = 1;
            }

            if (count($range) < 4) {
                $range[3] = '+';
            }

            list($one, $two, $count, $ar) = $range;


            $return = '<?php' . " for ({$var} = {$one};{$var} <= {$two};{$var}{$ar}={$count}):" . ' ?>';
            $this->com[md5($match[0])] = $return;
            return $return;
        }, $content);
    }

    /**
     * foreach方法
     * @param string $content
     * @return mixed
     */
    public function foreachoCommand($content = '')
    {
        return preg_replace_callback('/@foro(?:[\s])([\w\W]*?)in(?:[\s])([\w\W]*?)(?:[\s]*)\:|@endfor;/is', function ($match) {
            if (isset($this->com[md5($match[0])])) {
                return $this->com[md5($match[0])];
            }
            if (strpos($match[0], '@endfor') === 0) {
                return '<?php endforeach;endif; ?>';
            }
            if (strpos($match[1], ',')) {
                $match[1] = explode(',', $match[1]);
                foreach ($match[1] as &$v) {
                    $v = trim($v);
                }
                $match[1] = implode(' => $', $match[1]);
            }
            $match[1] = '$' . trim($match[1]);
            $match[2] = $this->parseVar($match[2]);
            // 转换变量
            //end
            $return = '<?php if(is_object(' . $match[2] . ')): $__LIST__ = ' . $match[2] . ';foreach ($__LIST__ as ' . $match[1] . '): ?>';
            $this->com[md5($match[0])] = $return;
            return $return;
        }, $content);
    }

    /**
     * 设置变量
     * @param string $content
     * @return mixed
     */
    public function setCommand($content = '')
    {
        return preg_replace_callback('/@set\s+([\w\W]*?)(?:[\s]+)=(?:[\s]+)(.*?);/i', function ($match) {
            if (isset($this->com[md5($match[0])])) {
                return $this->com[md5($match[0])];
            }
            $condition = '""';
            $var = $this->parseVar($match[1]);
            $condition = $this->parseVar($match[2]);

            $return = '<?php ' . $var . ' = ' . $condition . '; ?>';
            $this->com[md5($match[0])] = $return;
            return $return;
        }, $content);
    }

    /**
     * if elseif else 语句
     * @param string $content
     */
    public function ifCommand($content = '')
    {
        return preg_replace_callback('/@if(?:[\s])(.*?):|@elseif(?:[\s])(.*?):|@else:|@endif;/is', function ($match) {
            if (isset($this->com[md5($match[0])])) {
                return $this->com[md5($match[0])];
            }
            if (strpos($match[0], '@endif') === 0) {
                return '<?php endif; ?>';
            }
            if (strpos($match[0], '@else:') === 0) {
                return '<?php else: ?>';
            }
            if (strpos($match[0], '@else') === 0) {

                $condition = $this->parseConditionVar($match[2]);
                $condition = $this->parseCondition($condition);
                $return = '<?php elseif (' . $condition . '): ?>';
                $this->com[md5($match[0])] = $return;
                return $return;
            }

            $condition = $this->parseConditionVar($match[1]);
            $condition = $this->parseCondition($condition);
            $return = '<?php if (' . $condition . '): ?>';
            $this->com[md5($match[0])] = $return;
            return $return;
        }, $content);
    }

    protected function parseCondition($condition)
    {
        return str_replace([
            ' and ', ' not ', ' or ', ' is ',
        ], [' && ', ' != ', ' || ', ' == '], $condition);
    }

    protected function parseConditionVar($condition)
    {
        $condition = $this->parseCondition($condition);
        $filter = [
            ' && ', ' || '
        ];
        if (strpos($condition, '(') !== false) {
            $temp = false;
            foreach ($filter as $f) {
                if (strpos($condition, $f) !== false) {
                    $temp = true;
                    $condition = explode($f, $condition);
                    foreach ($condition as $key => $v) {
                        $condition[$key] = $this->parseVar($v);
                    }
                    $condition = implode($f, $condition);
                }
            }
            if ($temp) {
                return $condition;
            }
        }

        return str_replace(['$true', '$false'], ['true', 'false'], $this->parseVar($condition));
    }

    public function source($content = '') {
        return preg_replace_callback('/@res(.*?);/is', function ($m) {
            $match = $m[1];
            if (isset($this->com[md5($m[0])])) {
                return $this->com[md5($m[0])];
            }
            $match = explode(',', $match);
            $data = '';
            $img = ['jpeg', 'jpg', 'gif', 'png', 'bmp', 'webp', 'psd'];
            foreach ($match as $p) {
                if (empty($p)) {
                    continue;
                }
                if ( strtolower(pathinfo($p, PATHINFO_EXTENSION)) === 'css') {

                        $data .= '<link rel="stylesheet" href="<?php echo $tplconfig["public"] ?>' . trim($p) . '?v=<?php echo $tplconfig["version"]; ?>" />' . PHP_EOL;
                        continue;
                }

                if ( strtolower(pathinfo($p, PATHINFO_EXTENSION)) === 'js') {

                    $data .= '<script type="text/javascript" src="<?php echo $tplconfig["public"] ?>' . trim($p) . '?v=<?php echo $tplconfig["version"]; ?>"></script>' . PHP_EOL;
                    continue;
                }

                if (in_array(strtolower(pathinfo($p, PATHINFO_EXTENSION)), $img)) {
                    $data .= '<?php echo $tplconfig["public"] ?>' . trim($p) . '?v=<?php echo $tplconfig["version"]; ?>' . PHP_EOL;
                    break;
                }
            }
            $this->com[md5($m[0])] = $data;
            return $data;
        }, $content);
    }


    public function actionCommand($content = '') {
        return preg_replace_callback('/@action\((.*?)\);/is', function ($m) {

            if (isset($this->com[md5($m[0])])) {
                return $this->com[md5($m[0])];
            }

            $match = $m[1];
            $content = 'do_action(' . $match . ')';

            $content = $this->parseFunction($content);
            $this->com[md5($m[0])] = $content = "<?php {$content}; ?>";
            return $content;
        },$content);
    }

    public function filterCommand($content = '') {
        return preg_replace_callback('/@filter\((.*?)\)\s+as\s+(.*?);|@filter\((.*?)\);/is', function ($m) {
            if (!empty($m[1])) {
                $match = $m[1];
            }

            if (!empty($m[3])) {
                $match = $m[3];
            }
            $content = 'do_filter(' . $match . ')';

            $content = $this->parseFunction($content);
            if (empty($m[2])) {
                $this->com[md5($m[0])] = $content = "<?php {$content}; ?>";
            } else {
                $this->com[md5($m[0])] = $content = $this->parseVar($m[2]) . " = {$content};";
            }
            return $content;
        },$content);
    }
}
